package com.kz.framework.helper;

import com.kz.framework.annotation.MyAutowired;
import com.kz.framework.annotation.aop.MyAfter;
import com.kz.framework.annotation.aop.MyAround;
import com.kz.framework.annotation.aop.MyBefore;
import com.kz.framework.proxy.JdkDynamicAopProxy;
import com.kz.framework.proxy.ProxyChain;
import com.kz.framework.utils.ClassUtil;
import com.kz.framework.utils.ReflectionUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author zhgp
 * @version 1.0
 * @description: TODO
 * @date 2021/6/19 20:44
 */
public class BeanHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(BeanHelper.class);
    /**
     * 存放bean的容器
     */
    private static final Map<Class<?>, Object> BEAN_MAP = new HashMap();

    /**
     * 拦截规则 存储注解对应的规则链
     */
    public static final Map<Class<?>, List<ProxyChain>> PROXY_RULE = new ConcurrentHashMap<>();

    static {
        try {
            parseAspectjClass();
        } catch (Exception e) {
            throw new RuntimeException("aop 切面切面解析失败....................");
        }
        beanInstantiateAndFieldPour();

    }

    public static Class<?> findImplementClass(Class<?> interfaceClass) {
        Class<?> implementClass = null;
        /**
         * 获取接口下的所有实现类
         */
        Set<Class<?>> classSetBySuper = ClassHelper.getClassSetBySuper(interfaceClass);
        if (CollectionUtils.isNotEmpty(classSetBySuper)) {
            /**
             * 如果不止一个实现类则按照name属性注入
             */
            if (classSetBySuper.size() > 1) {
                MyAutowired autowired = interfaceClass.getAnnotation(MyAutowired.class);
                String className = autowired.className();
                if (!StringUtils.isBlank(className)) {
                    for (Class<?> cls : classSetBySuper) {
                        if (className.equals(cls.getName())) {
                            implementClass = cls;
                            break;
                        }
                    }
                }
            } else {
                implementClass = classSetBySuper.iterator().next();
            }
        } else {
            throw new RuntimeException("您注入的bean没有实现类，请实现.............");
        }
        return implementClass;
    }

    /**
     * @description: 获取一个bean
     * @author zhgp
     * @date 2021/6/20 12:16
     * @version 1.0
     */
    public static Object getBean(Class<?> cls) {
        return BEAN_MAP.get(cls);
    }

    /**
     * @description: TODO
     * @param beanClass
     * @param isProxy 是否需要代理
     * @return: void
     * @author zhgp
     * @date: 2021/7/3 11:04
     */
    private static void createBean(Class beanClass, boolean isProxy)
        throws IllegalAccessException, InstantiationException {
        Class<?>[] interfaces = ClassUtil.getInterfaces(beanClass);
        Object target = beanClass.newInstance();
        LOGGER.info("实例化?bean......................." + beanClass.getName());
        /**
         * 判断是否需要代理
         */
        if (isProxy && interfaces != null && interfaces.length > 0) {
            // 创建jdk动态代理的对象
            JdkDynamicAopProxy myJdkDynamicAopProxy = new JdkDynamicAopProxy(target);
            // 返回代理对象
            target = Proxy.newProxyInstance(ClassUtil.getClassLoader(), interfaces, myJdkDynamicAopProxy);
            LOGGER.info("bean......................." + beanClass.getName() + " 需要被代理 目前只支持jdk动态代理 所以必须实现 interfaces");
            // 存储再缓存池
            BEAN_MAP.put(beanClass, target);
        } else {
            // 不需要代理也存储再缓存池
            BEAN_MAP.put(beanClass, target);
        }
    }

    /**
     * @description: 判断类是否需要代理
     * @param targetClass
     * @return: boolean
     * @author zhgp
     * @date: 2021/7/3 10:47
     */
    private static boolean hasProxy(Class targetClass) {
        // 获取所有的方法
        Method[] declaredMethods = targetClass.getDeclaredMethods();
        for (Method method : Arrays.asList(declaredMethods)) {
            // 获取方法上的注解
            Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
            // 当方法存在注释的时候 开始判断是否方法需要被代理
            if (declaredAnnotations != null && declaredAnnotations.length > 0) {
                for (Annotation annotation : Arrays.asList(declaredAnnotations)) {
                    // 如果解析规则存在这个注解就返回true
                    if (PROXY_RULE.containsKey(annotation.annotationType())) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * @description: 解析切面配置生成代理映射<key 切面注解,value 代理链>
     * @param
     * @return: void
     * @author zhgp
     * @date: 2021/7/2 23:17
     */
    private static void parseAspectjClass() throws IllegalAccessException, InstantiationException {

        /**
         * 遍历所有切面配置的类
         */
        for (Class aClass : ClassHelper.getAspectClassSet()) {
            Method[] declaredMethods = aClass.getDeclaredMethods();
            for (Method method : declaredMethods) {
                /**
                 * 获取MyAround注解，后续实现其它的
                 */
                Annotation[] annotations = method.getAnnotations();
                if (annotations.length == 1) {
                    Annotation annotation = annotations[0];
                    /**
                     * 获取切面注解
                     */
                    Class<? extends Annotation> targetMethod = null;
                    if (annotation instanceof MyAround) {
                        targetMethod = ((MyAround)annotation).targetMethod();
                    }
                    if (annotation instanceof MyBefore) {
                        targetMethod = ((MyBefore)annotation).targetMethod();
                    }
                    if (annotation instanceof MyAfter) {
                        targetMethod = ((MyAfter)annotation).targetMethod();
                    }
                    if (targetMethod == null) {
                        continue;
                    }
                    /**
                     * 获得切面配置类实例
                     */
                    Object proxyTarget = aClass.newInstance();
                    /**
                     * 构建一次拦截，也就是一个方法可能会有多次代理增强
                     */
                    ProxyChain proxyChain = new ProxyChain(method, proxyTarget);
                    /**
                     * 如果存在则加入调用链
                     */
                    if (PROXY_RULE.containsKey(targetMethod)) {
                        PROXY_RULE.get(targetMethod).add(proxyChain);
                    } else {
                        List<ProxyChain> proxyChains = new ArrayList<>();
                        proxyChains.add(proxyChain);
                        PROXY_RULE.put(targetMethod, proxyChains);
                    }
                }
            }
        }
    }

    private static void beanInstantiateAndFieldPour() {
        /**
         * 加载项目中所有需要注册到容器中的class
         */
        Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet();

        /**
         * 实例化存入容器中
         */
        try {
            for (Class<?> beanClass : beanClassSet) {
                createBean(beanClass, hasProxy(beanClass));
            }
        } catch (Exception e) {
            LOGGER.error(" bean 初始化失败..................", e);
            return;
        }

        /**
         * 设置bean中注入的成员变量 实现IOC功能
         */
        if (BEAN_MAP.isEmpty()) {
            return;
        }
        for (Map.Entry<Class<?>, Object> entry : BEAN_MAP.entrySet()) {
            Class<?> beanClass = entry.getKey();
            Object beanInstance = entry.getValue();
            Field[] beanFields = beanClass.getDeclaredFields();// 暴力反射获取属性列表
            for (Field field : beanFields) {
                // 判断是否带有注入属性的注解
                if (!field.isAnnotationPresent(MyAutowired.class)) {
                    continue;
                }
                Class<?> filedClass = field.getType();
                /**
                 * 判断容器中是否存在这个类型的bean 不存在则去查找它的子类和实现类
                 */
                if (!BEAN_MAP.containsKey(filedClass)) {
                    // 获取它的实现类
                    filedClass = findImplementClass(filedClass);
                }
                // 获取Class类对应的实例
                Object beanFieldInstance = BEAN_MAP.get(filedClass);
                if (beanFieldInstance != null) {
                    // 将bean注入成员变量中
                    try {
                        ReflectionUtil.setField(beanInstance, field, beanFieldInstance);
                        LOGGER.info(beanClass.getName() + " 注入bean-------> " + filedClass.getName());
                    } catch (IllegalArgumentException e) {
                        LOGGER.error(beanClass.getName() + " 注入 " + filedClass.getName()
                            + "失败，可能原因：无法将jdk代理对象注入非interface 类型成员变量！ 请试着将变量类型替换为其interface ", e);
                        throw e;
                    }
                } else {
                    throw new RuntimeException("bean 注入失败....................请检查");
                }
            }

        }
    }
}
